/* dialog.c */

#include <stdlib.h>

#include "SDL.h"
#include "SDL_image.h"
#include "SDL_gfxPrimitives.h"

#include "dialog.h"

static void dlg_setup(DIALOG *dlg, SDL_Surface *screen)
{
  int i;

  dlg->num_widgets = 0;
  for (i = 0; i < DLG_MAX_WIDGETS; i++)
    if (dlg->widget[i].func == NULL) {
      dlg->num_widgets = i;
      break;
    }

  dlg->stop = 0;
  dlg->focus = NULL;
  dlg->screen = screen;
  for (i = 0; i < dlg->num_widgets; i++)
    dlg->widget[i].dlg = dlg;
}

void dlg_end(DIALOG *dlg)
{
  dlg->stop = 1;
}

int dlg_w_edit_get_text_start(DLG_WIDGET *widget)
{
  int widget_size = widget->w - 6;
  int txt_len = strlen((char *) widget->p);

  if (txt_len + 1 > widget_size/8) {
    int start = txt_len + 1 - widget_size/8;
    if (start < 0)
      return 0;
    return start;
  }
  return 0;
}

DLG_WIDGET *dlg_find_widget_at(DIALOG *dlg, int x, int y)
{
  int i;

  for (i = dlg->num_widgets - 1; i >= 0; i--) {
    DLG_WIDGET *w = dlg->widget + i;
    if (x >= w->x && y >= w->y && x <= w->x + w->w && y <= w->y + w->h)
      return w;
  }
  return NULL;
}

void dlg_advance_focus(DIALOG *dlg)
{
  int n_focus;
  int abort_change_focus = 0;

  /* remove old focus */
  if (dlg->focus != NULL) {
    DLG_WIDGET *tmp;

    n_focus = dlg->focus - dlg->widget;
    tmp = dlg->focus;
    dlg->focus = NULL;
    if (dlg_send_msg(tmp, DLGM_LOSE_FOCUS, 0, 0, NULL))
      abort_change_focus = 1;
  } else
    n_focus = -1;

  if (! abort_change_focus) {
    /* advance focus */
    n_focus = (n_focus + 1) % dlg->num_widgets;
    
    /* set new focus */
    dlg->focus = dlg->widget + n_focus;
    dlg_send_msg(dlg->focus, DLGM_GAIN_FOCUS, 0, 0, NULL);
  }
}

int dlg_msg_is_button_press(DLG_WIDGET *widget, int msg, unsigned int p1, unsigned int p2, void *p)
{
  if (msg == DLGM_SDL_EVENT) {
    SDL_Event *ev = (SDL_Event *) p;
    if (ev->type == SDL_MOUSEBUTTONUP
	&& dlg_find_widget_at(widget->dlg, ev->button.x, ev->button.y) == widget->dlg->focus)
      return 1;
  }
  return 0;
}

int dlg_send_msg(DLG_WIDGET *widget, int msg, unsigned int p1, unsigned int p2, void *p)
{
  int ret;

  if (msg == DLGM_REDRAW) {
    SDL_Rect rect = { widget->x, widget->y, widget->w, widget->h };
    SDL_SetClipRect(widget->dlg->screen, &rect);
  }

  /* call processing function */
  ret = widget->func(widget, msg, p1, p1, p);

  if (msg == DLGM_REDRAW)
    SDL_SetClipRect(widget->dlg->screen, NULL);

  if (ret == 0) {
    /* message not handled, let's process it */
    switch (msg) {
    case DLGM_SDL_EVENT:
      {
	SDL_Event *ev = (SDL_Event *) p;
	if (ev->type == SDL_KEYDOWN && ev->key.keysym.sym == SDLK_TAB)
	  dlg_advance_focus(widget->dlg);
      }
      break;
    }
  } else {
    /* message was handled, let's update the dialog */
    switch (msg) {
    case DLGM_REDRAW:
      widget->dlg->update_screen = 1;
      break;
    }
  }

  return ret;
}

void dlg_broadcast_msg(DIALOG *dlg, int msg, unsigned int p1, unsigned int p2, void *p)
{
  int i;

  for (i = 0; i < dlg->num_widgets; i++)
    dlg_send_msg(dlg->widget + i, msg, p1, p2, p);
}

int dlg_run(DIALOG *dlg, SDL_Surface *screen, int initial_focus)
{
  dlg_setup(dlg, screen);

  dlg_broadcast_msg(dlg, DLGM_INIT, 0, 0, NULL);
  dlg_broadcast_msg(dlg, DLGM_REDRAW, 0, 0, NULL);

  if (initial_focus >= 0 && initial_focus < dlg->num_widgets) {
    dlg->focus = dlg->widget + initial_focus;
    dlg_send_msg(dlg->focus, DLGM_GAIN_FOCUS, 0, 0, NULL);
  }

  while (! dlg->stop) {
    SDL_Event ev;
    DLG_WIDGET *widget;

    while (SDL_PollEvent(&ev)) {
      switch (ev.type) {
      case SDL_QUIT:
	dlg_broadcast_msg(dlg, DLGM_DESTROY, 0, 0, NULL);
	return 1;
	
      case SDL_MOUSEBUTTONDOWN:
	{
	  DLG_WIDGET *old_focus = dlg->focus;
	  int abort_change_focus = 0;

	  widget = dlg_find_widget_at(dlg, ev.button.x, ev.button.y);
	  if (widget == NULL)
	    break;

	  /* remove old focus */
	  if (widget != old_focus) {
	    if (old_focus != NULL) {
	      dlg->focus = NULL;
	      if (dlg_send_msg(old_focus, DLGM_LOSE_FOCUS, 0, 0, NULL))
		abort_change_focus = 1;
	    }
	    
	    /* gain new focus */
	    if (! abort_change_focus) {
	      dlg->focus = widget;
	      dlg_send_msg(widget, DLGM_GAIN_FOCUS, 0, 0, NULL);
	    }
	  }

	  /* send SDL event */
	  dlg_send_msg(widget, DLGM_SDL_EVENT, 0, 0, &ev);
	}
	break;

      case SDL_MOUSEBUTTONUP:
	if (dlg->focus != NULL)
	  dlg_send_msg(dlg->focus, DLGM_SDL_EVENT, 0, 0, &ev);
	break;

      case SDL_MOUSEMOTION:
	if (dlg->focus != NULL)
	  dlg_send_msg(dlg->focus, DLGM_SDL_EVENT, 0, 0, &ev);
	break;

      case SDL_KEYDOWN:
      case SDL_KEYUP:
	if (dlg->focus != NULL)
	  dlg_send_msg(dlg->focus, DLGM_SDL_EVENT, 0, 0, &ev);
	break;

      default:
	break;
      }
    }

    if (dlg->update_screen) {
      SDL_Flip(dlg->screen);
      dlg->update_screen = 0;
    }

    dlg_broadcast_msg(dlg, DLGM_IDLE, 0, 0, NULL);
  }

  dlg_broadcast_msg(dlg, DLGM_DESTROY, 0, 0, NULL);
  return 0;
}

/* --------------------------------------------------------- */
/* --- widgets --------------------------------------------- */

/*
 * d3: background color
 */
int dlg_w_screen(DLG_WIDGET *w, int msg, unsigned int p1, unsigned int p2, void *p)
{
  switch (msg) {
  case DLGM_INIT:
    w->x = w->y = 0;
    w->w = w->dlg->screen->w;
    w->h = w->dlg->screen->h;
    if (w->note_func != NULL)
      w->note_func(w, DLGM_INIT);
    break;

  case DLGM_REDRAW:
    SDL_FillRect(w->dlg->screen, NULL, w->d3 >> 8);
    break;
  }

  return 0;
}

/*
 * d2: text color
 * d3: background color
 */
int dlg_w_label(DLG_WIDGET *w, int msg, unsigned int p1, unsigned int p2, void *p)
{
  switch (msg) {
  case DLGM_INIT:
    w->w = 8 * strlen((char *) w->p);
    w->h = 8;
    if (w->d2 == 0 && w->d3 == 0) {
      w->d2 = 0xffffffff;
      w->d3 = w->dlg->widget[0].d3;
    }
    return 1;

  case DLGM_REDRAW:
    SDL_FillRect(w->dlg->screen, NULL, w->d3 >> 8);
    stringColor(w->dlg->screen, w->x, w->y, (char *) w->p, w->d2);
    return 1;
  }

  return 0;
}

/*
 * d2: text color
 * d3: background color
 */
int dlg_w_text(DLG_WIDGET *w, int msg, unsigned int p1, unsigned int p2, void *p)
{
  switch (msg) {
  case DLGM_INIT:
    w->h = 8;
    if (w->d2 == 0 && w->d3 == 0) {
      w->d2 = 0xffffffff;
      w->d3 = w->dlg->widget[0].d3;
    }
    return 1;

  default:
    return dlg_w_label(w, msg, p1, p2, p);
  }
}

/*
 * d1: 0=button is up, 1=button is down
 */
int dlg_w_button(DLG_WIDGET *w, int msg, unsigned int p1, unsigned int p2, void *p)
{
  switch (msg) {
  case DLGM_INIT:
    w->h = 18;
    w->d1 = 0;
    return 1;

  case DLGM_SDL_EVENT:
    {
      SDL_Event *ev = (SDL_Event *) p;
      if (ev->type == SDL_MOUSEBUTTONDOWN) {
	w->d1 = 1;
	dlg_send_msg(w, DLGM_REDRAW, 0, 0, NULL);
      } else if (ev->type == SDL_MOUSEBUTTONUP) {
	w->d1 = 0;
	dlg_send_msg(w, DLGM_REDRAW, 0, 0, NULL);
        if (w->note_func != NULL
            && dlg_find_widget_at(w->dlg, ev->button.x, ev->button.y) == w)
          w->note_func(w, DLGN_CLICKED);
      }
    }
    return 1;

  case DLGM_REDRAW:
    {
      unsigned int c1 = 0xccccccff;
      unsigned int c2 = 0x888888ff;

      SDL_FillRect(w->dlg->screen, NULL, 0xaaaaaa);

      vlineColor(w->dlg->screen, w->x,   w->y,        w->y+w->h-1, (w->d1) ? c2 : c1);
      vlineColor(w->dlg->screen, w->x+1, w->y+1,      w->y+w->h-2, (w->d1) ? c2 : c1);
      hlineColor(w->dlg->screen, w->x,   w->x+w->w-1, w->y,        (w->d1) ? c2 : c1);
      hlineColor(w->dlg->screen, w->x+1, w->x+w->w-2, w->y+1,      (w->d1) ? c2 : c1);

      vlineColor(w->dlg->screen, w->x+w->w-1, w->y,        w->y+w->h-1, (w->d1) ? c1 : c2);
      vlineColor(w->dlg->screen, w->x+w->w-2, w->y+1,      w->y+w->h-2, (w->d1) ? c1 : c2);
      hlineColor(w->dlg->screen, w->x,        w->x+w->w-1, w->y+w->h-1, (w->d1) ? c1 : c2);
      hlineColor(w->dlg->screen, w->x+1,      w->x+w->w-2, w->y+w->h-2, (w->d1) ? c1 : c2);

      stringColor(w->dlg->screen, w->x + 5, w->y + 5, (char *) w->p, (w->d1) ? 0x444444ff : 0xffffffff);
    }
    return 1;

  default:
    return dlg_w_label(w, msg, p1, p2, p);
  }
}

/*
 * d1: text max length
 * d2: text start position on window
 */
int dlg_w_edit(DLG_WIDGET *w, int msg, unsigned int p1, unsigned int p2, void *p)
{
  switch (msg) {
  case DLGM_INIT:
    w->h = 14;
    w->d2 = dlg_w_edit_get_text_start(w);
    return 1;

  case DLGM_SDL_EVENT:
    {
      SDL_Event *ev = (SDL_Event *) p;

      switch (ev->type) {
      case SDL_KEYDOWN:
	switch (ev->key.keysym.sym) {
	case SDLK_BACKSPACE:
	  {
	    char *str = (char *) w->p;
	    int l = strlen(str);
	    if (l > 0)
	      l--;
	    str[l] = '\0';
	    w->d2 = dlg_w_edit_get_text_start(w);
            if (w->note_func != NULL)
              w->note_func(w, DLGN_CHANGED);
	    dlg_send_msg(w, DLGM_REDRAW, 0, 0, NULL);
	  }
	  return 1;

        case SDLK_RETURN:
          if (w->note_func != NULL)
            w->note_func(w, DLGN_ENTER);
          return 1;

	default:
	  {
	    char *str = (char *) w->p;
	    int l = strlen(str);

	    if (ev->key.keysym.unicode >= 32
		&& ev->key.keysym.unicode < 127
		&& l < w->d1-1) {
	      str[l++] = ev->key.keysym.unicode;
	      str[l] = '\0';
	      w->d2 = dlg_w_edit_get_text_start(w);
              if (w->note_func != NULL)
                w->note_func(w, DLGN_CHANGED);
	      dlg_send_msg(w, DLGM_REDRAW, 0, 0, NULL);
	    }
	  }
	  return 1;
	}
	break;
      }
    }
    return 0;

  case DLGM_REDRAW:
    {
      unsigned int c1 = 0xccccccff;
      unsigned int c2 = 0x888888ff;

      SDL_FillRect(w->dlg->screen, NULL, 0x444444);

      vlineColor(w->dlg->screen, w->x,   w->y,        w->y+w->h-1, c2);
      hlineColor(w->dlg->screen, w->x,   w->x+w->w-1, w->y,        c2);

      vlineColor(w->dlg->screen, w->x+w->w-1, w->y,        w->y+w->h-1, c1);
      hlineColor(w->dlg->screen, w->x,        w->x+w->w-1, w->y+w->h-1, c1);

      stringColor(w->dlg->screen, w->x + 3, w->y + 3, ((char *) w->p) + w->d2, 0xffffffff);
      if (w->dlg->focus == w)
	stringColor(w->dlg->screen, w->x + (strlen((char *) w->p)-w->d2)*8 + 3, w->y + 3, "_", 0xff0000ff);
    }
    return 1;

  case DLGM_LOSE_FOCUS:
  case DLGM_GAIN_FOCUS:
    dlg_send_msg(w, DLGM_REDRAW, 0, 0, NULL);
    return 0;

  default:
    return dlg_w_label(w, msg, p1, p2, p);
  }
}

/*
 * d1: index of selected item
 * d2: height of item list
 * d3: 0=show current item, 1=show list
 */
int dlg_w_dropdown(DLG_WIDGET *w, int msg, unsigned int p1, unsigned int p2, void *p)
{
  switch (msg) {
  case DLGM_INIT:
    w->h = 14;
    if (w->d2 < w->h)
      w->d2 = 104;
    return 1;

  case DLGM_SDL_EVENT:
    {
      SDL_Event *ev = (SDL_Event *) p;

      switch (ev->type) {
      case SDL_MOUSEBUTTONDOWN:
	w->d3 = 1;
	dlg_send_msg(w, DLGM_REDRAW, 0, 0, NULL);
        break;

      case SDL_MOUSEBUTTONUP:
	w->d3 = 0;
        if (w->note_func != NULL)
          w->note_func(w, DLGN_CHANGED);
	dlg_broadcast_msg(w->dlg, DLGM_REDRAW, 0, 0, NULL);
        break;

      case SDL_MOUSEMOTION:
        if (w->d3) {
          int sel_pos = ev->motion.y - w->y;

          if (sel_pos >= 0) {
            char **items = (char **) w->p;
            int i;

            for (i = 0; items[i] != NULL; i++) {
              if (i == sel_pos / 10)
                break;
            }
            if (items[i] != NULL) {
              w->d1 = i;
              dlg_send_msg(w, DLGM_REDRAW, 0, 0, NULL);
            }
          }
        }
        break;
      }
    }
    return 1;

  case DLGM_REDRAW:
    if (w->d3) {
      int i;
      char **items = (char **) w->p;
      SDL_Rect clip = { w->x, w->y, w->w, w->d2 };

      SDL_SetClipRect(w->dlg->screen, &clip);
      SDL_FillRect(w->dlg->screen, NULL, 0xaaaaaa);

      vlineColor(w->dlg->screen, w->x,   w->y,        w->y+w->d2-1, 0xffffffff);
      hlineColor(w->dlg->screen, w->x,   w->x+w->w-1, w->y,        0xffffffff);

      vlineColor(w->dlg->screen, w->x+w->w-1, w->y,        w->y+w->d2-1, 0xffffffff);
      hlineColor(w->dlg->screen, w->x,        w->x+w->w-1, w->y+w->d2-1, 0xffffffff);

      for (i = 0; items[i] != NULL; i++) {
        if (i == w->d1) {
          SDL_Rect rc = { w->x + 1, w->y + 10*i + 2, w->w - 2, 9 };
          SDL_FillRect(w->dlg->screen, &rc, 0xffffffff);
          stringColor(w->dlg->screen, w->x + 3, w->y + 10*i + 3, items[i], 0xff0000ff);
        } else
          stringColor(w->dlg->screen, w->x + 3, w->y + 10*i + 3, items[i], 0xffffffff);
      }
        
    } else {
      unsigned int c1 = 0xccccccff;
      unsigned int c2 = 0x888888ff;
      char **items = (char **) w->p;

      SDL_FillRect(w->dlg->screen, NULL, 0x444444);

      vlineColor(w->dlg->screen, w->x,   w->y,        w->y+w->h-1, c2);
      hlineColor(w->dlg->screen, w->x,   w->x+w->w-1, w->y,        c2);

      vlineColor(w->dlg->screen, w->x+w->w-1, w->y,        w->y+w->h-1, c1);
      hlineColor(w->dlg->screen, w->x,        w->x+w->w-1, w->y+w->h-1, c1);

      if (items[w->d1] != NULL)
        stringColor(w->dlg->screen, w->x + 3, w->y + 3, items[w->d1], 0xffffffff);
    }
    return 1;

  default:
    break;
  }
  return 0;
}
